//********************************************************************************************************************//
//ORGANIZATION:
//AUTHOR: bpeng(bogers.peng@gmail.com)
//SUMMARY:
//********************************************************************************************************************//
#include "../FilePath.h"
#include "System/Base/Exceptions.h"
#include "System/Base/IteratorBuilder.h"

using namespace Native::System;
using namespace Native::System::Platform;

bool ValidateDepth(const size_t& point, const CRange<size_t>& range)
{
    return range.InCloseRange(point);
}

CString CFilePath::_ToDirectyString(size_t depth) const
{
    auto validate_depth = [](const size_t& point, const CRange<size_t>& range) {
        return range.InCloseRange(point);
    };
    COutOfRangeException<size_t>::Check(
        NATIVE_SCENE(L"_ToDirectyString"),
        depth,
        CRange<size_t>(0, Depth()),
        validate_depth, L"depth");

    CString directy;
    auto iterator = UArrayIteratorBuilder<CString>::CreateForward(&_directory);

    NATIVE_FOREACH(&iterator, each) {
        if (depth == 0) {
            break;
        }

        directy.Append(each->ToString());
        directy.Append(_division);

        depth--;
    }

    return move(directy);
}

CFilePath::CFilePath()
{
}

CFilePath::CFilePath(C_STRING& path, wchar_t division) :
    _division(division)
{
    CEmptyStringException::Check(NATIVE_SCENE(L"CFilePath"), path, L"path");

    bool has_filename = path.Tail() != _division;
    auto items = path.Split(division);

    if (has_filename) {
        _filename = items[items.size()-1];

        if(items.size() == 1) {
            _directory.Append(L".");
            _relative = true;
        } else {
            _relative = path.Head() == L'.';

            for (int i = 0; i < items.size()-1; i++) {
                if (!items[i].Empty()) {
                    _directory.Append(items[i]);
                }
            }
        }
    } else {
        _relative = path.Head() == L'.';

        for (auto item : items) {
            if (!item.Empty()) {
                _directory.Append(item);
            }
        }
    }
}

CFilePath::CFilePath(C_STRING& directory, C_STRING& filename, wchar_t division) throw() :
    _division(division)
{
    if (directory.Empty() && filename.Empty()) {
        throw CEmptyStringException(
            NATIVE_SCENE(L"CFilePath"),
            L"directory,filename",
            L"Both directory and filename are empty");
    }

    _filename = filename;

    CString directory_string(directory);

    if (directory_string.Empty()) {
        directory_string.Append(L".");
    }

    _relative = directory_string.Head() == L'.';

    auto items = directory_string.Split(division);

    for(auto item : items) {
        if (!item.Empty()) {
            _directory.Append(item);
        }
    }
}

CFilePath::CFilePath(C_FILE_PATH& that) :
    CObject(that),
    _directory(that._directory),
    _filename(that._filename),
    _division(that._division),
    _relative(that._relative)
{
}

CFilePath::CFilePath(C_FILE_PATH&& that) :
    CObject(that),
    _directory(that._directory),
    _filename(that._filename),
    _division(that._division),
    _relative(that._relative)
{
}

CFilePath::~CFilePath()
{
}

size_t CFilePath::Depth() const
{
    return _directory.Count();
}

bool CFilePath::Relative() const
{
    return _relative;
}

CString CFilePath::Path() const
{
    CString path = _ToDirectyString(Depth());

    if (!_filename.Empty()) {
        path.Append(_filename);
    }

    return move(path);
}

CString CFilePath::Filename() const
{
    return _filename;
}

CString CFilePath::Directory() const
{
    return move(_ToDirectyString(Depth()));
}

CString CFilePath::ParentDirectory() const
{
    CString directy = _ToDirectyString(Depth()-1);

    return move(directy);
}

C_FILE_PATH& CFilePath::operator=(C_FILE_PATH& rvalue)
{
    CObject::operator=(rvalue);

    _directory = rvalue._directory;
    _filename = rvalue._filename;
    _division = rvalue._division;
    _relative = rvalue._relative;

    return *this;
}